/*
 * Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.pig4cloud.pig.ads.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.api.client.util.Lists;
import com.pig4cloud.pig.ads.config.SdkProperties;
import com.pig4cloud.pig.ads.main3399.mapper.OeAccesstokenMapper;
import com.pig4cloud.pig.ads.pig.mapper.TtAccesstokenMapper;
import com.pig4cloud.pig.ads.service.AdAccountService;
import com.pig4cloud.pig.ads.service.AdvertiserService;
import com.pig4cloud.pig.ads.service.TtAccesstokenService;
import com.pig4cloud.pig.ads.utils.OEHttpUtils;
import com.pig4cloud.pig.api.dto.AdverRoleDto;
import com.pig4cloud.pig.api.entity.*;
import com.pig4cloud.pig.api.util.Constants;
import com.pig4cloud.pig.api.util.JsonUtil;
import com.pig4cloud.pig.api.vo.AdAccountAgentVo.Advertiser;
import com.pig4cloud.pig.api.vo.AuthCodeReqVo;
import com.pig4cloud.pig.common.core.constant.enums.PlatformTypeEnum;
import com.xxl.job.core.Exception.BusinessException;
import com.xxl.job.core.biz.model.ReturnT;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;


/**
 * @广告账户 服务实现类
 * @author john
 *
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class TtAccesstokenServiceImpl extends ServiceImpl<TtAccesstokenMapper, TtAccesstoken> implements TtAccesstokenService {

	private Logger logger = LoggerFactory.getLogger(TtAccesstokenServiceImpl.class);

	private final TtAccesstokenMapper ttAccesstokenMapper;

	private final AdvertiserService advertiserService;

	private final OeAccesstokenMapper oeAccesstokenMapper;

	private final AdAccountService adAccountService;

	private final SdkProperties sdkProperties;


	@Autowired
	private StringRedisTemplate stringRedisTemplate;

	@Value("${accesstoken_url}")
	private String accesstokenUrl;

	@Value("${adver_get_url}")
	private String adverGetUrl;



	@Override
	public List<Advertiser> saveAuth(AuthCodeReqVo authVo) {
		final List<Advertiser> advertiserList = new ArrayList<>();
		String appId = authVo.getApp_id();
		String secret = authVo.getSecret();
		String authCode = authVo.getAuth_code();
		Map<String, Object> data = new HashMap<String, Object>();
    	data.put("app_id", appId);
    	data.put("secret", secret);
    	data.put("grant_type", "auth_code");
    	data.put("auth_code", authCode);
    	String resultStr = OEHttpUtils.doPost(accesstokenUrl, data);
    	ResponseBean resBean = JSON.parseObject(resultStr, ResponseBean.class);
    	if(resBean != null && "0".equals(resBean.getCode())){
    		JSONObject jsonObj = resBean.getData();
    		try {
				jsonObj = JsonUtil.toSnakeObject(jsonObj.toJSONString(), JSONObject.class);
			} catch (IOException e) {
				logger.error("驼峰转换失败!");
			}
    		logger.info("====>jsonObj={}", jsonObj.toJSONString());
    		TtAccesstoken token = JSON.toJavaObject(jsonObj, TtAccesstoken.class);
    		log.info("=====>token={}", JSON.toJSONString(token));
    		token.setAppId(appId);
    		token.setSecret(secret);
    		token.setAuthCode(authCode);
    		//获取账号权限
    		List<AdverRoleDto> list = this.fetchRole(token, appId, secret);
    		// 同步广告账户
			this.saveAccount(list, advertiserList, token.getAccessToken());
		}
    	return advertiserList;
	}

	public void saveAccount(List<AdverRoleDto> list, List<Advertiser> advertiserList, String accessToken ){
		// 已经加挂的广告账户
		List<AdAccount> saveAccountList = Lists.newArrayList();
		List<Map> admap = Lists.newArrayList();
		QueryWrapper<AdAccount> wrapperAcc = new QueryWrapper<>();
		wrapperAcc.eq("media_code",PlatformTypeEnum.TT.getValue());
		wrapperAcc.eq("is_delete",0);
		List<AdAccount> accountsList = adAccountService.list(wrapperAcc);
		// 已加挂 管家
		List<String> houseidList = accountsList.stream().filter(a->a.getHousekeeper().equals(a.getAdvertiserId())).map(AdAccount::getAdvertiserId).collect(Collectors.toList());
		//新增到用户绑定表 存主账号
		list.stream().filter(a-> "false".equals(a.getIs_valid()) || !houseidList.contains(a.getAdvertiser_id())).forEach(item -> {
			// 新增广告账户表   管家账户也需要插入广告账户表
			AdAccount adAccount = new AdAccount();
			adAccount.setMediaCode(PlatformTypeEnum.TT.getValue());
			adAccount.setMediaName(PlatformTypeEnum.TT.getDescription());
			adAccount.setAdvertiserId(item.getAdvertiser_id());
			adAccount.setAdvertiserName(item.getAdvertiser_name());
			adAccount.setHousekeeper(item.getAdvertiser_id());
			adAccount.setEffectiveStatus("false".equals(item.getIs_valid())?2:1);
			adAccount.setIsDelete(0);
			adAccount.setCreateTime(new Date());
			adAccount.setUpdateTime(new Date());
			saveAccountList.add(adAccount);
		});
		for (AdverRoleDto item : list) {
			if (Objects.nonNull(item.getAdvertiser_role()) && 6 == item.getAdvertiser_role()){
				continue;
			}
			String housekeeper = item.getAdvertiser_id();
			// 拉去头条管家账户下子账户列表
			List<AdAccount> accountList = Lists.newArrayList();
			Map<String, Object> data = new HashMap<String, Object>();
			data.put("advertiser_id", housekeeper);
			String resultStr = OEHttpUtils.doGet(sdkProperties.getAdvertiserListUrl(), data, accessToken);
			//logger.info("获取账户管家下的广告主ID列表返回=={}, adAccount={}", resultStr, adAccount);
			ResponseBean resBean = JSON.parseObject(resultStr, ResponseBean.class);
			if (resBean != null && "0".equals(resBean.getCode())) {
				JSONObject jsonObj = resBean.getData();
				String listStr = jsonObj.getString("list");
				List<Map> listMap = JSON.parseObject(listStr, new TypeReference<List<Map>>() {});
				for (Map map : listMap) {
					String advertiserId = String.valueOf(map.get("advertiser_id"));
					String advertiserName = String.valueOf(map.get("advertiser_name"));//广告主名称
					map.put("housekeeper",housekeeper);
					map.put("deleted","0");
					AdAccount adAccount = new AdAccount();
					adAccount.setAdvertiserId(advertiserId);
					adAccount.setAdvertiserName(advertiserName);
					accountList.add(adAccount);
				}
				admap.addAll(listMap);
			} else {
				logger.error("头条授权-获取广告账户失败:{}",JSON.toJSONString(resBean));
				throw new BusinessException(resBean != null ? resBean.getCode() : String.valueOf(ReturnT.FAIL.getCode()), resBean != null ? resBean.getMessage() : "");
			}
			if (CollectionUtil.isNotEmpty(accountList)){
				for (AdAccount account : accountList){
					// 保存加挂的广告账户，建立广告账户与代理商的关系
					advertiserList.add(new Advertiser(account.getAdvertiserId(), account.getAdvertiserName()));
					List<AdAccount> accountsArr = accountsList.stream().filter(v -> v.getAdvertiserId().equals(account.getAdvertiserId())).collect(Collectors.toList());
					if (CollectionUtil.isNotEmpty(accountsArr)){
						AdAccount adAccount = accountsArr.get(0);
						adAccount.setAdvertiserName(account.getAdvertiserName());
						adAccount.setHousekeeper(housekeeper);
						adAccount.setEffectiveStatus(1);
						adAccount.setUpdateTime(new Date());
						saveAccountList.add(adAccount);
					}else{
						AdAccount adAccount = new AdAccount();
						adAccount.setMediaCode(PlatformTypeEnum.TT.getValue());
						adAccount.setMediaName(PlatformTypeEnum.TT.getDescription());
						adAccount.setAdvertiserId(account.getAdvertiserId());
						adAccount.setAdvertiserName(account.getAdvertiserName());
						adAccount.setHousekeeper(housekeeper);
						adAccount.setIsDelete(0);
						adAccount.setCreateTime(new Date());
						adAccount.setUpdateTime(new Date());
						saveAccountList.add(adAccount);
					}
				}
			}
			List<String> ids = accountList.stream().map(AdAccount::getAdvertiserId).collect(Collectors.toList());
			accountsList.stream().filter(a-> housekeeper.equals(a.getHousekeeper()) && !ids.contains(a.getAdvertiserId())&& !housekeeper.equals(a.getAdvertiserId())).forEach(b->{
				b.setEffectiveStatus(2);
				b.setUpdateTime(new Date());
				saveAccountList.add(b);
				Map<String, String> map = new HashMap<>();
				map.put("housekeeper",housekeeper);
				map.put("deleted","1");
				map.put("advertiser_id",b.getAdvertiserId());
				map.put("advertiser_name",b.getAdvertiserName());
				admap.add(map);
			});
		}
		if (CollectionUtil.isNotEmpty(saveAccountList)){
			//批量更新插入
			adAccountService.saveUpdateList(saveAccountList);
		}
		if (CollectionUtil.isNotEmpty(admap)) {
			advertiserService.saveAdvertiser(admap, PlatformTypeEnum.TT.getValue());
		}
	}


	private List<AdverRoleDto> fetchRole(TtAccesstoken accesstoken, String appId, String secret) {
		List<AdverRoleDto> adList = new ArrayList<>();

		Map<String, Object> data = new HashMap<String, Object>();
		data.put("access_token", accesstoken.getAccessToken());
    	data.put("secret", secret);
    	data.put("app_id", appId);
    	String resultStr = OEHttpUtils.doGet(adverGetUrl, data, accesstoken.getAccessToken());
    	logger.info("通过新接口获取已授权账户,resultStr=={}, secret=={},app_id=={}", resultStr, secret, appId);
    	ResponseBean resBean = JSON.parseObject(resultStr, ResponseBean.class);
    	if(resBean != null && "0".equals(resBean.getCode())){
    		JSONObject jsonObject = resBean.getData();
    		String listStr = jsonObject.getString("list");
    		List<AdverRoleDto> list = JSONObject.parseArray(listStr,  AdverRoleDto.class);

    		Date now = new Date();
    		for(AdverRoleDto adInfo: list){
    			String adAccount = adInfo.getAdvertiser_id();
    			//目前该字段也将在不久的将来下线。后期使用account_role
    			Integer advertiser_role = adInfo.getAdvertiser_role();//广告主角色，1-普通广告主，2-账号管家，3-一级代理商，4-二级代理商
    			//授权有效性
    			String isValid = adInfo.getIs_valid();
    			if("false".equals(isValid)) {
					adList.add(adInfo);
    				continue;
    			}
    			adList.add(adInfo);
    			TtAccesstoken token = ttAccesstokenMapper.selectById(adAccount);
    			if(token != null) {
    				BeanUtils.copyProperties(accesstoken, token);
    				token.setAdAccount(adAccount);
    				token.setHousekeeper(advertiser_role);
        			token.setAccountRole(adInfo.getAccount_role());
        			token.setTime(now.getTime()/1000);
        			token.setIssyn("N");//还未同步
        			ttAccesstokenMapper.updateById(token);
    			}else {
    				//新增
    				token = new TtAccesstoken();
        			BeanUtils.copyProperties(accesstoken, token);
        			token.setAdAccount(adAccount);
        			token.setHousekeeper(advertiser_role);
        			token.setAccountRole(adInfo.getAccount_role());
        			token.setTime(now.getTime()/1000);
        			token.setIssyn("N");//还未同步
        			ttAccesstokenMapper.insert(token);
    			}
    			//更新redis
				stringRedisTemplate.opsForValue().set(Constants.OCEANENGINE_REDIS_TOKEN_KEY_PRIX_ + adAccount, JSON.toJSONString(token));

				//更新3399由投放平台维护
				OeAccesstoken entity = new OeAccesstoken();
				entity.setAd_account(adAccount);
				entity.setRefreshByPig("Y");
				oeAccesstokenMapper.updateById(entity);
    		}
    	}
		return adList;
	}


	/**
	 * @普通账号获取token
	 */
	@Override
	public String fetchAccesstoken(String adAccount) {
		if(StringUtils.isBlank(adAccount)) {
			return null;
		}
		String accesstoken = stringRedisTemplate.opsForValue().get(Constants.OCEANENGINE_REDIS_ADVER_TOKEN_KEY_PRIX_ + adAccount);
		if(StringUtils.isNotBlank(accesstoken)) {
			return accesstoken;
		}
		AccountToken accountToken = advertiserService.getAdvertiserToken(PlatformTypeEnum.TT.getValue(),adAccount);
		if(accountToken == null || StringUtils.isBlank(accountToken.getHousekeeper())) {
			return null;
		}
		return accountToken.getAccessToken();
	}

}
